package com.carnation.qa.socketserver.proxy.moi;

import java.io.InputStream;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.carnation.metaone.mgr.exception.MOException;
import com.carnation.metaone.sm.exception.SMException;
import com.carnation.mong.platform.domain.MOIDomain;
import com.carnation.mong.platform.domain.MOIIniter;
import com.carnation.mong.platform.domain.MOIRelationType;
import com.carnation.mong.platform.exception.ConnectionInvalidException;
import com.carnation.mong.platform.exception.PasswordInvalidException;
import com.carnation.mong.platform.exception.SessionException;
import com.carnation.mong.platform.service.AnalysisService;
import com.carnation.mong.platform.service.Connection;
import com.carnation.mong.platform.service.DRelationService;
import com.carnation.mong.platform.service.DataRevisionService;
import com.carnation.mong.platform.service.DataService;
import com.carnation.mong.platform.service.DomainService;
import com.carnation.mong.platform.service.MOISession;
import com.carnation.mong.platform.service.MRelationService;
import com.carnation.mong.platform.service.ModelAttributeService;
import com.carnation.mong.platform.service.PackageService;
import com.carnation.mong.platform.service.Query;
import com.carnation.mong.platform.service.RelationTypeService;
import com.carnation.mong.platform.service.SecurityService;
import com.carnation.mong.platform.service.SessionFactory;

/**
 * @author Nick
 * @since 2.0
 * 
 */
public class ServiceFactory {

	private static String IP, USERNAME, PASSWORD;
	private static int PORT;
	private static ServiceFactory self;
	private static Map<String, Object> services = new HashMap<String, Object>();
	private static Map<String, MOIRelationType> relationTypes = new HashMap<String, MOIRelationType>();
	private final Log log = LogFactory.getLog(ServiceFactory.class);

	static {
		self = new ServiceFactory();
		init();
	}

	private ServiceFactory() {

	};

	private static void init() {
		Properties prop = null;
		try {
			prop = new Properties();
			InputStream in = ServiceFactory.class.getClassLoader()
					.getResourceAsStream("socketserver.properties");
			prop.load(in);
			if (in != null) {
				in.close();
			}
		} catch (Exception e) {
		}
		IP = getValue(prop, "mong.connection.ip", "localhost");
		PORT = new Integer(getValue(prop, "mong.connection.port", "1099"));
		USERNAME = getValue(prop, "mong.connection.username", "admin");
		PASSWORD = getValue(prop, "mong.connection.password", "admin");
		if (self.log.isInfoEnabled()) {
			self.log.info("mong server ip=" + IP + ",port=" + PORT
					+ ",username=" + USERNAME + ",password=" + PASSWORD);
		}
	}

	private static String getValue(Properties prop, String key,
			String defaultValue) {
		try {
			return new String(prop.getProperty(key));
		} catch (Exception e) {
			if (self.log.isWarnEnabled()) {
				self.log.warn("Could not find configuration " + "[" + key
						+ "];" + " using defaults.");
			}
			return defaultValue;
		}
	}

	/**
	 * 获取服务。
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getService(Class<T> clazz) {
		if (clazz == null)
			return null;
		T rule = null;
		final String className = clazz.getName();
		if (services.containsKey(className)) {
			rule = (T) services.get(className);
		} else {
			rule = (T) createService(className);
			services.put(className, rule);
		}
		return rule;
	}

	/**
	 * 获取模型关系类型
	 * 
	 * @param moiRelationType
	 * @return
	 */
	public static MOIRelationType getMRelationType(String moiRelationType) {
		if (relationTypes.containsKey(moiRelationType)) {
			return relationTypes.get(moiRelationType);
		} else {
			RelationTypeService rts = getService(RelationTypeService.class);
			MOIRelationType relationType = null;
			try {
				relationType = rts.getRTByPath(moiRelationType);
				relationTypes.put(moiRelationType, relationType);
				return relationType;
			} catch (RemoteException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			} catch (MOException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			}
			if (self.log.isErrorEnabled()) {
				self.log.error(moiRelationType);
			}
			throw new NullPointerException();
		}

	}

	/**
	 * 更新Domain
	 * 
	 * @param domain
	 */
	public static void refreshDomain(MOIDomain domain) {
		System.currentTimeMillis();
		try {
			getService(Connection.class).setDomain(domain);
		} catch (RemoteException e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		} catch (SessionException e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		} catch (ConnectionInvalidException e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		} catch (MOException e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		}
	}

	public static void commit() {
		try {
			getService(Connection.class).commit();
		} catch (Exception e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		}
	}
	
	public static void close() {
		try {
			getService(Connection.class).close();
		} catch (Exception e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		}
	}

	public static Boolean reConnect() {
		Connection conn = getService(Connection.class);
		try {
			conn.close();
		} catch (RemoteException e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		} catch (MOException e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		}
		services.clear();
		Connection connection = getConnection();
		if (connection == null) {
			return Boolean.FALSE;
		} else {
			services.put(Connection.class.getName(), connection);
			return Boolean.TRUE;
		}

	}

	private static Object createService(final String className) {
		if (className != null) {
			Connection connection = getConnection();
			if (connection == null) {
				throw new NullPointerException("Connection is null.");
			} else {
				if (!services.containsKey(Connection.class.getName())) {
					services.put(Connection.class.getName(), connection);
				}
			}
			try {
				return getService(connection, className);
			} catch (RemoteException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			} catch (NotBoundException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			} catch (SessionException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			}
		}
		if (self.log.isErrorEnabled()) {
			self.log.error(className);
		}
		throw new NullPointerException(className);

	}

	private static Connection getConnection() {
		Connection connection = null;
		if (services.containsKey(Connection.class.getName())) {
			connection = (Connection) services.get(Connection.class.getName());
		} else {
			try {
				MOIIniter.init(IP, PORT);
				SessionFactory sf = MOIIniter.createSessionFactory();
				MOISession session = sf.createSession(USERNAME, PASSWORD);
				connection = session.createConnection();
				services.put(SessionFactory.class.getName(), sf);
				services.put(MOISession.class.getName(), session);
			} catch (RemoteException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			} catch (SMException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			} catch (PasswordInvalidException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			} catch (SessionException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			} catch (NotBoundException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			} catch (MOException e) {
				if (self.log.isErrorEnabled()) {
					self.log.error(e.getMessage(), e);
				}
			}
		}
		return connection;
	}

	private static Object getService(Connection connection, String className)
			throws RemoteException, NotBoundException, SessionException {
		try {
			if (className.equals(AnalysisService.class.getName())) {
				return connection.createAnalysisService();
			} else if (className.equals(Connection.class.getName())) {
				return connection;
			} else if (className.equals(DataRevisionService.class.getName())) {
				return connection.createDataRevisionService();
			} else if (className.equals(DataService.class.getName())) {
				return connection.createDataService();
			} else if (className.equals(DomainService.class.getName())) {
				return connection.createDomainService();
			} else if (className.equals(DRelationService.class.getName())) {
				return connection.createDRelationService();
			} else if (className.equals(ModelAttributeService.class.getName())) {
				return connection.createModelAttributeService();
			} else if (className.equals(MOISession.class.getName())) {
				return connection.getSession();
			} else if (className.equals(MRelationService.class.getName())) {
				return connection.createMRelationService();
			} else if (className.equals(PackageService.class.getName())) {
				return connection.createPackageService();
			} else if (className.equals(Query.class.getName())) {
				return connection.createQuery();
			} else if (className.equals(RelationTypeService.class.getName())) {
				return connection.createRelationTypeService();
			} else if (className.equals(SecurityService.class.getName())) {
				return getService(MOISession.class).createSecurityService();
			}
		} catch (MOException e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		} catch (RemoteException e) {
			if (self.log.isErrorEnabled()) {
				self.log.error(e.getMessage(), e);
			}
		}
		
		throw new NullPointerException(className);
	}

}
